home *** CD-ROM | disk | FTP | other *** search
/ Tech Arsenal 1 / Tech Arsenal (Arsenal Computer).ISO / tek-01 / ue312src.zip / LINE.C < prev    next >
C/C++ Source or Header  |  1992-12-21  |  29KB  |  1,198 lines

  1. /*
  2.  * The functions in this file are a general set of line management utilities.
  3.  * They are the only routines that touch the text. They also touch the buffer
  4.  * and window structures, to make sure that the necessary updating gets done.
  5.  * There are routines in this file that handle the kill buffer too. It isn't
  6.  * here for any good reason.
  7.  *
  8.  * Note that this code only updates the dot and mark values in the window list.
  9.  * Since all the code acts on the current window, the buffer that we are
  10.  * editing must be being displayed, which means that "b_nwnd" is non zero,
  11.  * which means that the dot and mark values in the buffer headers are nonsense.
  12.  */
  13.  
  14. #include    <stdio.h>
  15. #include    "estruct.h"
  16. #include    "eproto.h"
  17. #include    "edef.h"
  18. #include    "elang.h"
  19.  
  20. #define    BSIZE(a)    (a + NBLOCK - 1) & (~(NBLOCK - 1))
  21.  
  22. static long last_size = -1L;    /* last # of bytes yanked */
  23.  
  24. /*
  25.  * This routine allocates a block of memory large enough to hold a LINE
  26.  * containing "used" characters. Return a pointer to the new block, or
  27.  * NULL if there isn't any memory left. Print a message in the message
  28.  * line if no space.
  29.  */
  30.  
  31. LINE *PASCAL NEAR lalloc(used)
  32.  
  33. register int used;
  34.  
  35. {
  36.     register LINE    *lp;
  37.  
  38.     if ((lp = (LINE *)malloc(sizeof(LINE)+used)) == NULL) {
  39.         mlabort(TEXT94);
  40. /*                      "%%Out of memory" */
  41.         return(NULL);
  42.     }
  43.     lp->l_size = used;
  44.     lp->l_used = used;
  45. #if    WINDOW_MSWIN
  46.     {
  47.         static int o = 0;
  48.         if (--o < 0) {
  49.             longop(TRUE);
  50.             o = 10;     /* to lower overhead, only 10% calls to longop */
  51.         }
  52.     }
  53. #endif
  54.     return(lp);
  55. }
  56.  
  57. /*
  58.  * Delete line "lp". Fix all of the links that might point at it (they are
  59.  * moved to offset 0 of the next line. Unlink the line from whatever buffer it
  60.  * might be in. Release the memory. The buffers are updated too; the magic
  61.  * conditions described in the above comments don't hold here.
  62.  */
  63. PASCAL NEAR lfree(lp)
  64. register LINE    *lp;
  65. {
  66.     register BUFFER *bp;
  67.     SCREEN *scrp;        /* screen to fix pointers in */
  68.     register WINDOW *wp;
  69.     register int cmark;        /* current mark */
  70.  
  71.     /* in all screens.... */
  72.     scrp = first_screen;
  73.     while (scrp) {
  74.         wp = scrp->s_first_window;
  75.         while (wp != NULL) {
  76.             if (wp->w_linep == lp)
  77.                 wp->w_linep = lforw(lp);
  78.             if (wp->w_dotp    == lp) {
  79.                 wp->w_dotp  = lforw(lp);
  80.                 wp->w_doto  = 0;
  81.             }
  82.             for (cmark = 0; cmark < NMARKS; cmark++) {
  83.                 if (wp->w_markp[cmark] == lp) {
  84.                     wp->w_markp[cmark] = lforw(lp);
  85.                     wp->w_marko[cmark] = 0;
  86.                 }
  87.             }
  88.             wp = wp->w_wndp;
  89.         }
  90.  
  91.         /* next screen! */
  92.         scrp = scrp->s_next_screen;
  93.     }
  94.  
  95.     bp = bheadp;
  96.     while (bp != NULL) {
  97.         if (bp->b_nwnd == 0) {
  98.             if (bp->b_dotp    == lp) {
  99.                 bp->b_dotp = lforw(lp);
  100.                 bp->b_doto = 0;
  101.             }
  102.             for (cmark = 0; cmark < NMARKS; cmark++) {
  103.                 if (bp->b_markp[cmark] == lp) {
  104.                     bp->b_markp[cmark] = lforw(lp);
  105.                     bp->b_marko[cmark] = 0;
  106.                 }
  107.             }
  108.         }
  109.         bp = bp->b_bufp;
  110.     }
  111.     lp->l_bp->l_fp = lp->l_fp;
  112.     lp->l_fp->l_bp = lp->l_bp;
  113.     free((char *) lp);
  114. #if    WINDOW_MSWIN
  115.     {
  116.         static int o = 0;
  117.         if (--o < 0) {
  118.             longop(TRUE);
  119.             o = 10;     /* to lower overhead, only 10% calls to longop */
  120.         }
  121.     }
  122. #endif
  123. }
  124.  
  125. /*
  126.  * This routine gets called when a character is changed in place in the current
  127.  * buffer. It updates all of the required flags in the buffer and window
  128.  * system. The flag used is passed as an argument; if the buffer is being
  129.  * displayed in more than 1 window we change EDIT t HARD. Set MODE if the
  130.  * mode line needs to be updated (the "*" has to be set).
  131.  */
  132. PASCAL NEAR lchange(flag)
  133. register int    flag;
  134. {
  135.     register WINDOW *wp;
  136.     SCREEN *scrp;        /* screen to fix pointers in */
  137.  
  138.     if (curbp->b_nwnd != 1)         /* Ensure hard.     */
  139.         flag = WFHARD;
  140.     if ((curbp->b_flag&BFCHG) == 0) {    /* First change, so    */
  141.         flag |= WFMODE;         /* update mode lines.    */
  142.         curbp->b_flag |= BFCHG;
  143.     }
  144.  
  145.     /* in all screens.... */
  146.     scrp = first_screen;
  147.     while (scrp) {
  148.         /* make sure all the needed windows get this flag */
  149.         wp = scrp->s_first_window;
  150.         while (wp != NULL) {
  151.             if (wp->w_bufp == curbp)
  152.                 wp->w_flag |= flag;
  153.             wp = wp->w_wndp;
  154.         }
  155.  
  156.         /* next screen! */
  157.         scrp = scrp->s_next_screen;
  158.     }
  159. }
  160.  
  161. PASCAL NEAR insspace(f, n)    /* insert spaces forward into text */
  162.  
  163. int f, n;    /* default flag and numeric argument */
  164.  
  165. {
  166.     register int    status;
  167.  
  168.     if ((status = linsert(n, ' ')) == TRUE)
  169.         status = backchar(f, n);
  170.     return status;
  171. }
  172.  
  173. /*
  174.  * linstr -- Insert a string at the current point
  175.  */
  176.  
  177. int PASCAL NEAR linstr(instr)
  178. char    *instr;
  179. {
  180.     register int status;
  181.  
  182.     status = TRUE;
  183.     if (instr != NULL)
  184.         while (*instr) {
  185.             status = ((*instr == '\r') ? lnewline(): linsert(1, *instr));
  186.  
  187.             /* Insertion error? */
  188.             if (status != TRUE) {
  189.                 mlwrite(TEXT168);
  190. /*                                      "%%Can not insert string" */
  191.                 break;
  192.             }
  193.             instr++;
  194.         }
  195.     return(status);
  196. }
  197.  
  198. /*
  199.  * Insert "n" copies of the character "c" at the current location of dot. In
  200.  * the easy case all that happens is the text is stored in the line. In the
  201.  * hard case, the line has to be reallocated. When the window list is updated,
  202.  * take special care; I screwed it up once. You always update dot in the
  203.  * current window. You update mark, and a dot in another window, if it is
  204.  * greater than the place where you did the insert. Return TRUE if all is
  205.  * well, and FALSE on errors.
  206.  */
  207.  
  208. #if    PROTO
  209. PASCAL NEAR linsert(int n, char c)
  210. #else
  211. PASCAL NEAR linsert(n, c)
  212.  
  213. int    n;
  214. char    c;
  215. #endif
  216.  
  217. {
  218.     register char    *cp1;
  219.     register char    *cp2;
  220.     register LINE    *lp1;
  221.     register LINE    *lp2;
  222.     register LINE    *lp3;
  223.     register int    doto;
  224.     register int    i;
  225.     register WINDOW *wp;
  226.     SCREEN *scrp;        /* screen to fix pointers in */
  227.     int cmark;        /* current mark */
  228.  
  229.     if (curbp->b_mode&MDVIEW)    /* don't allow this command if    */
  230.         return(rdonly());    /* we are in read only mode    */
  231.  
  232.     /* a zero insert means do nothing! */
  233.     if (n == 0)
  234.         return(TRUE);
  235.  
  236.     /* Negative numbers of inserted characters are right out! */
  237.     if (n < 1)
  238.         return(FALSE);
  239.  
  240.     /* mark the current window's buffer as changed */
  241.     lchange(WFEDIT);
  242.  
  243.     lp1 = curwp->w_dotp;            /* Current line     */
  244.     if (lp1 == curbp->b_linep) {        /* At the end: special    */
  245.         if (curwp->w_doto != 0) {
  246.             mlwrite(TEXT170);
  247. /*                              "bug: linsert" */
  248.             return(FALSE);
  249.         }
  250.         if ((lp2=lalloc(BSIZE(n))) == NULL)    /* Allocate new line    */
  251.             return(FALSE);
  252.         lp2->l_used = n;
  253.         lp3 = lp1->l_bp;        /* Previous line    */
  254.         lp3->l_fp = lp2;        /* Link in        */
  255.         lp2->l_fp = lp1;
  256.         lp1->l_bp = lp2;
  257.         lp2->l_bp = lp3;
  258.         for (i=0; i<n; ++i)
  259.             lp2->l_text[i] = c;
  260.         curwp->w_dotp = lp2;
  261.         curwp->w_doto = n;
  262.         return(TRUE);
  263.     }
  264.     doto = curwp->w_doto;            /* Save for later.    */
  265.     if (lp1->l_used+n > lp1->l_size) {    /* Hard: reallocate    */
  266.         if ((lp2=lalloc(BSIZE(lp1->l_used+n))) == NULL)
  267.             return(FALSE);
  268.         lp2->l_used = lp1->l_used+n;
  269.         cp1 = &lp1->l_text[0];
  270.         cp2 = &lp2->l_text[0];
  271.         while (cp1 != &lp1->l_text[doto])
  272.             *cp2++ = *cp1++;
  273.         cp2 += n;
  274.         while (cp1 != &lp1->l_text[lp1->l_used])
  275.             *cp2++ = *cp1++;
  276.         lp1->l_bp->l_fp = lp2;
  277.         lp2->l_fp = lp1->l_fp;
  278.         lp1->l_fp->l_bp = lp2;
  279.         lp2->l_bp = lp1->l_bp;
  280.         free((char *) lp1);
  281.     } else {                /* Easy: in place    */
  282.         lp2 = lp1;            /* Pretend new line    */
  283.         lp2->l_used += n;
  284.         cp2 = &lp1->l_text[lp1->l_used];
  285.         cp1 = cp2-n;
  286.         while (cp1 != &lp1->l_text[doto])
  287.             *--cp2 = *--cp1;
  288.     }
  289.     for (i=0; i<n; ++i)            /* Add the characters    */
  290.         lp2->l_text[doto+i] = c;
  291.     /* in all screens.... */
  292.     scrp = first_screen;
  293.     while (scrp) {
  294.  
  295.         wp = scrp->s_first_window;
  296.         while (wp != NULL) {
  297.             if (wp->w_linep == lp1)
  298.                 wp->w_linep = lp2;
  299.             if (wp->w_dotp == lp1) {
  300.                 wp->w_dotp = lp2;
  301.                 if (wp==curwp || wp->w_doto>doto)
  302.                     wp->w_doto += n;
  303.             }
  304.             for (cmark = 0; cmark < NMARKS; cmark++) {
  305.                 if (wp->w_markp[cmark] == lp1) {
  306.                     wp->w_markp[cmark] = lp2;
  307.                     if (wp->w_marko[cmark] > doto)
  308.                         wp->w_marko[cmark] += n;
  309.                 }
  310.             }
  311.             wp = wp->w_wndp;
  312.         }
  313.  
  314.         /* next screen! */
  315.         scrp = scrp->s_next_screen;
  316.     }
  317.     return(TRUE);
  318. }
  319.  
  320. /*
  321.  * Overwrite a character into the current line at the current position
  322.  *
  323.  */
  324.  
  325. #if    PROTO
  326. PASCAL NEAR lowrite(char c)
  327. #else
  328. PASCAL NEAR lowrite(c)
  329.  
  330. char c;        /* character to overwrite on current position */
  331. #endif
  332.  
  333. {
  334.     if (curwp->w_doto < curwp->w_dotp->l_used &&
  335.         (lgetc(curwp->w_dotp, curwp->w_doto) != '\t' ||
  336.          (curwp->w_doto) % 8 == 7))
  337.             ldelete(1L, FALSE);
  338.     return(linsert(1, c));
  339. }
  340.  
  341. /*
  342.  * lover -- Overwrite a string at the current point
  343.  */
  344.  
  345. int PASCAL NEAR lover(ostr)
  346.  
  347. char    *ostr;
  348.  
  349. {
  350.     register int status = TRUE;
  351.  
  352.     if (ostr != NULL)
  353.         while (*ostr && status == TRUE) {
  354.             status = ((*ostr == '\r') ? lnewline(): lowrite(*ostr));
  355.  
  356.             /* Insertion error? */
  357.             if (status != TRUE) {
  358.                 mlwrite(TEXT172);
  359. /*                                      "%%Out of memory while overwriting" */
  360.                 break;
  361.             }
  362.             ostr++;
  363.         }
  364.     return(status);
  365. }
  366.  
  367. /*
  368.  * Insert a newline into the buffer at the current location of dot in the
  369.  * current window. The funny ass-backwards way it does things is not a botch;
  370.  * it just makes the last line in the file not a special case. Return TRUE if
  371.  * everything works out and FALSE on error (memory allocation failure). The
  372.  * update of dot and mark is a bit easier then in the above case, because the
  373.  * split forces more updating.
  374.  */
  375. int PASCAL NEAR lnewline()
  376. {
  377.     register char    *cp1;
  378.     register char    *cp2;
  379.     register LINE    *lp1;
  380.     register LINE    *lp2;
  381.     register int    doto;
  382.     register WINDOW *wp;
  383.     SCREEN *scrp;        /* screen to fix pointers in */
  384.     int cmark;        /* current mark */
  385.  
  386.     if (curbp->b_mode&MDVIEW)    /* don't allow this command if    */
  387.         return(rdonly());    /* we are in read only mode    */
  388.     lchange(WFHARD);
  389.     lp1  = curwp->w_dotp;            /* Get the address and    */
  390.     doto = curwp->w_doto;            /* offset of "."    */
  391.     if ((lp2=lalloc(doto)) == NULL)     /* New first half line    */
  392.         return(FALSE);
  393.     cp1 = &lp1->l_text[0];            /* Shuffle text around    */
  394.     cp2 = &lp2->l_text[0];
  395.     while (cp1 != &lp1->l_text[doto])
  396.         *cp2++ = *cp1++;
  397.     cp2 = &lp1->l_text[0];
  398.     while (cp1 != &lp1->l_text[lp1->l_used])
  399.         *cp2++ = *cp1++;
  400.     lp1->l_used -= doto;
  401.     lp2->l_bp = lp1->l_bp;
  402.     lp1->l_bp = lp2;
  403.     lp2->l_bp->l_fp = lp2;
  404.     lp2->l_fp = lp1;
  405.  
  406.     /* in all screens.... */
  407.     scrp = first_screen;
  408.     while (scrp) {
  409.  
  410.         wp = scrp->s_first_window;
  411.         while (wp != NULL) {
  412.             if (wp->w_linep == lp1)
  413.                 wp->w_linep = lp2;
  414.             if (wp->w_dotp == lp1) {
  415.                 if (wp->w_doto < doto)
  416.                     wp->w_dotp = lp2;
  417.                 else
  418.                     wp->w_doto -= doto;
  419.             }
  420.             for (cmark = 0; cmark < NMARKS; cmark++) {
  421.                 if (wp->w_markp[cmark] == lp1) {
  422.                     if (wp->w_marko[cmark] < doto)
  423.                         wp->w_markp[cmark] = lp2;
  424.                     else
  425.                         wp->w_marko[cmark] -= doto;
  426.                 }
  427.             }
  428.             wp = wp->w_wndp;
  429.         }
  430.  
  431.         /* next screen! */
  432.         scrp = scrp->s_next_screen;
  433.     }
  434.     return(TRUE);
  435. }
  436.  
  437. /*
  438.  
  439. LDELETE:
  440.  
  441.     This function deletes "n" bytes, starting at dot. Positive n
  442. deletes forward, negative n deletes backwords. It understands how to
  443. deal with end of lines, and with two byte characters. It returns TRUE
  444. if all of the characters were deleted, and FALSE if they were not
  445. (because dot ran into the buffer end). The "kflag" is TRUE if the text
  446. should be put in the kill buffer.
  447.  
  448. */
  449.  
  450. PASCAL NEAR ldelete(n, kflag)
  451.  
  452. long n;     /* # of chars to delete */
  453. int kflag;    /* put killed text in kill buffer flag */
  454.  
  455. {
  456.     register char    *cp1;
  457.     register char    *cp2;
  458.     register LINE    *dotp;
  459.     register int    doto;
  460.     register int    chunk;
  461.     register WINDOW *wp;
  462.     int cmark;        /* current mark */
  463.  
  464.     if (curbp->b_mode&MDVIEW)    /* don't allow this command if    */
  465.         return(rdonly());    /* we are in read only mode    */
  466.  
  467.     /* going Forward? */
  468.     if (n >= 0) {
  469.  
  470.         while (n > 0) {
  471. #if    DBCS
  472.             /* never start forward on a 2 byte char */
  473.             if (curwp->w_doto > 0 && is2byte(curwp->w_dotp->l_text,
  474.                 &curwp->w_dotp->l_text[curwp->w_doto - 1])) {
  475.                 curwp->w_doto--;
  476.                 n++;
  477.             }
  478. #endif
  479.             /* record the current point */
  480.             dotp = curwp->w_dotp;
  481.             doto = curwp->w_doto;
  482.     
  483.             /* can't delete past the end of the buffer */
  484.             if (dotp == curbp->b_linep)
  485.                 return(FALSE);
  486.     
  487.             /* find out how many chars to delete on this line */
  488.             chunk = dotp->l_used-doto;    /* Size of chunk.    */
  489.             if (chunk > n)
  490.                 chunk = n;
  491.     
  492.             /* if at the end of a line, merge with the next */
  493.             if (chunk == 0) {
  494.     
  495.                 /* flag that we are making a hard change */
  496.                 lchange(WFHARD);
  497.                 if (ldelnewline() == FALSE ||
  498.                     (kflag != FALSE &&
  499.                      kinsert(FORWARD, '\r')==FALSE))
  500.                     return(FALSE);
  501.                 --n;
  502.                 continue;
  503.             }
  504.     
  505.             /* flag the fact we are changing the current line */
  506.             lchange(WFEDIT);
  507.     
  508.             /* find the limits of the kill */
  509.             cp1 = &dotp->l_text[doto];
  510.             cp2 = cp1 + chunk;
  511. #if    DBCS
  512.             /* never leave half a character */
  513.             if (is2byte(dotp->l_text, cp2 - 1)) {
  514.                 ++chunk;
  515.                 ++cp2;
  516.                 ++n;
  517.             }
  518. #endif
  519.  
  520.             /* save the text to the kill buffer */
  521.             if (kflag != FALSE) {
  522.                 while (cp1 != cp2) {
  523.                     if (kinsert(FORWARD, *cp1) == FALSE)
  524.                         return(FALSE);
  525.                     ++cp1;
  526.                 }
  527.                 cp1 = &dotp->l_text[doto];
  528.             }
  529.     
  530.             /* copy what is left of the line upward */
  531.             while (cp2 != &dotp->l_text[dotp->l_used])
  532.                 *cp1++ = *cp2++;
  533.             dotp->l_used -= chunk;
  534.     
  535.             /* fix any other windows with the same text displayed */
  536.             wp = wheadp;
  537.             while (wp != NULL) {
  538.     
  539.                 /* reset the dot if needed */
  540.                 if (wp->w_dotp==dotp && wp->w_doto>=doto) {
  541.                     wp->w_doto -= chunk;
  542.                     if (wp->w_doto < doto)
  543.                         wp->w_doto = doto;
  544.                 }
  545.     
  546.                 /* reset any marks if needed */
  547.                 for (cmark = 0; cmark < NMARKS; cmark++) {
  548.                     if (wp->w_markp[cmark]==dotp && wp->w_marko[cmark]>=doto) {
  549.                         wp->w_marko[cmark] -= chunk;
  550.                         if (wp->w_marko[cmark] < doto)
  551.                             wp->w_marko[cmark] = doto;
  552.                     }
  553.                 }
  554.     
  555.                 /* onward to the next window */
  556.                 wp = wp->w_wndp;
  557.             }
  558.     
  559.             /* indicate we have deleted chunk characters */
  560.             n -= chunk;
  561.         }
  562.     } else {
  563.         while (n < 0) {
  564. #if    DBCS
  565.             /* never start backwards on the
  566.                1st of a 2 byte character */
  567.             if (curwp->w_doto > 1 && is2byte(curwp->w_dotp->l_text,
  568.                 &curwp->w_dotp->l_text[curwp->w_doto-1])) {
  569.                 curwp->w_doto++;
  570.                 n--;
  571.             }
  572. #endif
  573.             /* record the current point */
  574.             dotp = curwp->w_dotp;
  575.             doto = curwp->w_doto;
  576.     
  577.             /* can't delete past the beginning of the buffer */
  578.             if (dotp == lforw(curbp->b_linep) && (doto == 0))
  579.                 return(FALSE);
  580.     
  581.             /* find out how many chars to delete on this line */
  582.             chunk = doto;        /* Size of chunk.    */
  583.             if (chunk > -n)
  584.                 chunk = -n;
  585.     
  586.             /* if at the beginning of a line, merge with the last */
  587.             if (chunk == 0) {
  588.     
  589.                 /* flag that we are making a hard change */
  590.                 lchange(WFHARD);
  591.                 backchar(TRUE, 1);
  592.                 if (ldelnewline() == FALSE ||
  593.                     (kflag != FALSE &&
  594.                      kinsert(REVERSE, '\r')==FALSE))
  595.                     return(FALSE);
  596.                 ++n;
  597.                 continue;
  598.             }
  599.     
  600.             /* flag the fact we are changing the current line */
  601.             lchange(WFEDIT);
  602.     
  603.             /* find the limits of the kill */
  604.             cp1 = &dotp->l_text[doto];
  605.             cp2 = cp1 - chunk;
  606. #if    DBCS
  607.             if (is2byte(dotp->l_text, cp2 - 1)) {
  608.                 ++chunk;
  609.                 --cp2;
  610.                 ++n;
  611.             }
  612. #endif
  613.     
  614.             /* save the text to the kill buffer */
  615.             if (kflag != FALSE) {
  616.                 while (cp1 > cp2) {
  617.                     if (kinsert(REVERSE, *(--cp1)) == FALSE)
  618.                         return(FALSE);
  619.                 }
  620.                 cp1 = &dotp->l_text[doto];
  621.             }
  622.     
  623.             /* copy what is left of the line downward */
  624.             while (cp1 != &dotp->l_text[dotp->l_used])
  625.                 *cp2++ = *cp1++;
  626.             dotp->l_used -= chunk;
  627.             curwp->w_doto -= chunk;
  628.     
  629.             /* fix any other windows with the same text displayed */
  630.             wp = wheadp;
  631.             while (wp != NULL) {
  632.     
  633.                 /* reset the dot if needed */
  634.                 if (wp->w_dotp==dotp && wp->w_doto>=doto) {
  635.                     wp->w_doto -= chunk;
  636.                     if (wp->w_doto < doto)
  637.                         wp->w_doto = doto;
  638.                 }
  639.     
  640.                 /* reset any marks if needed */
  641.                 for (cmark = 0; cmark < NMARKS; cmark++) {
  642.                     if (wp->w_markp[cmark]==dotp && wp->w_marko[cmark]>=doto) {
  643.                         wp->w_marko[cmark] -= chunk;
  644.                         if (wp->w_marko[cmark] < doto)
  645.                             wp->w_marko[cmark] = doto;
  646.                     }
  647.                 }
  648.     
  649.                 /* onward to the next window */
  650.                 wp = wp->w_wndp;
  651.             }
  652.     
  653.             /* indicate we have deleted chunk characters */
  654.             n += chunk;
  655.         }
  656.     }
  657.     return(TRUE);
  658. }
  659.  
  660. /* getctext:    grab and return a string with the text of
  661.         the current line
  662. */
  663.  
  664. char *PASCAL NEAR getctext()
  665.  
  666. {
  667.     register LINE *lp;    /* line to copy */
  668.     register int size;    /* length of line to return */
  669.     register char *sp;    /* string pointer into line */
  670.     register char *dp;    /* string pointer into returned line */
  671.     char rline[NSTRING];    /* line to return */
  672.  
  673.     /* find the contents of the current line and its length */
  674.     lp = curwp->w_dotp;
  675.     sp = ltext(lp);
  676.     size = lused(lp);
  677.     if (size >= NSTRING)
  678.         size = NSTRING - 1;
  679.  
  680.     /* copy it across */
  681.     dp = rline;
  682.     while (size--)
  683.         *dp++ = *sp++;
  684.     *dp = 0;
  685.     return(rline);
  686. }
  687.  
  688. /* putctext:    replace the current line with the passed in text    */
  689.  
  690. PASCAL NEAR putctext(iline)
  691.  
  692. char *iline;    /* contents of new line */
  693.  
  694. {
  695.     register int status;
  696.  
  697.     /* delete the current line */
  698.     curwp->w_doto = 0;    /* starting at the beginning of the line */
  699.     if ((status = killtext(TRUE, 1)) != TRUE)
  700.         return(status);
  701.  
  702.     /* insert the new line */
  703.     if ((status = linstr(iline)) != TRUE)
  704.         return(status);
  705.     status = lnewline();
  706.     backline(TRUE, 1);
  707.     return(status);
  708. }
  709.  
  710. /*
  711.  * Delete a newline. Join the current line with the next line. If the next line
  712.  * is the magic header line always return TRUE; merging the last line with the
  713.  * header line can be thought of as always being a successful operation, even
  714.  * if nothing is done, and this makes the kill buffer work "right". Easy cases
  715.  * can be done by shuffling data around. Hard cases require that lines be moved
  716.  * about in memory. Return FALSE on error and TRUE if all looks ok. Called by
  717.  * "ldelete" only.
  718.  */
  719. int PASCAL NEAR ldelnewline()
  720. {
  721.     register char    *cp1;
  722.     register char    *cp2;
  723.     register LINE    *lp1;
  724.     register LINE    *lp2;
  725.     register LINE    *lp3;
  726.     register WINDOW *wp;
  727.     SCREEN *scrp;        /* screen to fix pointers in */
  728.     int cmark;        /* current mark */
  729.  
  730.     if (curbp->b_mode&MDVIEW)    /* don't allow this command if    */
  731.         return(rdonly());    /* we are in read only mode    */
  732.     lp1 = curwp->w_dotp;
  733.     lp2 = lp1->l_fp;
  734.     if (lp2 == curbp->b_linep) {        /* At the buffer end.    */
  735.         if (lp1->l_used == 0)        /* Blank line.        */
  736.             lfree(lp1);
  737.         return(TRUE);
  738.     }
  739.     if (lp2->l_used <= lp1->l_size-lp1->l_used) {
  740.         cp1 = &lp1->l_text[lp1->l_used];
  741.         cp2 = &lp2->l_text[0];
  742.         while (cp2 != &lp2->l_text[lp2->l_used])
  743.         *cp1++ = *cp2++;
  744.  
  745.         /* in all screens.... */
  746.         scrp = first_screen;
  747.         while (scrp) {
  748.  
  749.             wp = scrp->s_first_window;
  750.             while (wp != NULL) {
  751.                 if (wp->w_linep == lp2)
  752.                     wp->w_linep = lp1;
  753.                 if (wp->w_dotp == lp2) {
  754.                     wp->w_dotp  = lp1;
  755.                     wp->w_doto += lp1->l_used;
  756.                 }
  757.                 for (cmark = 0; cmark < NMARKS; cmark++) {
  758.                     if (wp->w_markp[cmark] == lp2) {
  759.                         wp->w_markp[cmark]  = lp1;
  760.                         wp->w_marko[cmark] += lp1->l_used;
  761.                     }
  762.                 }
  763.                 wp = wp->w_wndp;
  764.             }
  765.  
  766.             /* next screen! */
  767.             scrp = scrp->s_next_screen;
  768.         }
  769.  
  770.         lp1->l_used += lp2->l_used;
  771.         lp1->l_fp = lp2->l_fp;
  772.         lp2->l_fp->l_bp = lp1;
  773.         free((char *) lp2);
  774.         return(TRUE);
  775.     }
  776.     if ((lp3=lalloc(lp1->l_used+lp2->l_used)) == NULL)
  777.         return(FALSE);
  778.     cp1 = &lp1->l_text[0];
  779.     cp2 = &lp3->l_text[0];
  780.     while (cp1 != &lp1->l_text[lp1->l_used])
  781.         *cp2++ = *cp1++;
  782.     cp1 = &lp2->l_text[0];
  783.     while (cp1 != &lp2->l_text[lp2->l_used])
  784.         *cp2++ = *cp1++;
  785.     lp1->l_bp->l_fp = lp3;
  786.     lp3->l_fp = lp2->l_fp;
  787.     lp2->l_fp->l_bp = lp3;
  788.     lp3->l_bp = lp1->l_bp;
  789.  
  790.     /* in all screens.... */
  791.     scrp = first_screen;
  792.     while (scrp) {
  793.  
  794.         wp = scrp->s_first_window;
  795.         while (wp != NULL) {
  796.             if (wp->w_linep==lp1 || wp->w_linep==lp2)
  797.                 wp->w_linep = lp3;
  798.             if (wp->w_dotp == lp1)
  799.                 wp->w_dotp  = lp3;
  800.             else if (wp->w_dotp == lp2) {
  801.                 wp->w_dotp  = lp3;
  802.                 wp->w_doto += lp1->l_used;
  803.             }
  804.             for (cmark = 0; cmark < NMARKS; cmark++) {
  805.                 if (wp->w_markp[cmark] == lp1)
  806.                     wp->w_markp[cmark]  = lp3;
  807.                 else if (wp->w_markp[cmark] == lp2) {
  808.                     wp->w_markp[cmark]  = lp3;
  809.                     wp->w_marko[cmark] += lp1->l_used;
  810.                 }
  811.             }
  812.             wp = wp->w_wndp;
  813.         }
  814.  
  815.         /* next screen! */
  816.         scrp = scrp->s_next_screen;
  817.     }
  818.  
  819.     free((char *) lp1);
  820.     free((char *) lp2);
  821.     return(TRUE);
  822. }
  823.  
  824. /*    Add a new line to the end of the indicated buffer.
  825.     return FALSE if we run out of memory
  826.     note that this works on non-displayed buffers as well!
  827. */
  828.  
  829. #if    PROTO
  830. int PASCAL NEAR addline(BUFFER *bp, char *text)
  831. #else
  832. int PASCAL NEAR addline(bp, text)
  833.  
  834. BUFFER *bp;    /* buffer to add text to */
  835. char *text;    /* line to add */
  836. #endif
  837. {
  838.     register LINE    *lp;
  839.     register int    i;
  840.     register int    ntext;
  841.  
  842.     /* allocate the memory to hold the line */
  843.     ntext = strlen(text);
  844.     if ((lp=lalloc(ntext)) == NULL)
  845.         return(FALSE);
  846.  
  847.     /* copy the text into the new line */
  848.     for (i=0; i<ntext; ++i)
  849.         lputc(lp, i, text[i]);
  850.  
  851.     /* add the new line to the end of the buffer */
  852.     bp->b_linep->l_bp->l_fp = lp;
  853.     lp->l_bp = bp->b_linep->l_bp;
  854.     bp->b_linep->l_bp = lp;
  855.     lp->l_fp = bp->b_linep;
  856.  
  857.     /* if the point was at the end of the buffer,
  858.        move it to the beginning of the new line */
  859.     if (bp->b_dotp == bp->b_linep)
  860.         bp->b_dotp = lp;
  861.     return(TRUE);
  862. }
  863.  
  864. /*
  865.  * Delete all of the text saved in the kill buffer. Called by commands when a
  866.  * new kill context is being created. The kill buffer array is released, just
  867.  * in case the buffer has grown to immense size. No errors.
  868.  */
  869.  
  870. VOID PASCAL NEAR kdelete()
  871.  
  872. {
  873.     KILL *kp;    /* ptr to scan kill buffer chunk list */
  874.  
  875.     if (kbufh[kill_index] != NULL) {
  876.  
  877.         /* first, delete all the chunks */
  878.         kbufp[kill_index] = kbufh[kill_index];
  879.         while (kbufp[kill_index] != NULL) {
  880.             kp = kbufp[kill_index]->d_next;
  881.             free((char *)kbufp[kill_index]);
  882.             kbufp[kill_index] = kp;
  883. #if    WINDOW_MSWIN
  884.             {
  885.                 static int o = 0;
  886.                 if (--o < 0) {
  887.                     longop(TRUE);
  888.                     o = 10;     /* to lower overhead,
  889.                         only 10% calls to longop */
  890.                 }
  891.             }
  892. #endif
  893.         }
  894.  
  895.         /* and reset all the kill buffer pointers */
  896.         kbufh[kill_index] = kbufp[kill_index] = NULL;
  897.         kskip[kill_index] = 0;
  898.         kused[kill_index] = KBLOCK;             
  899.     }
  900. }
  901.  
  902. /*    next_kill:    advance to the next position in the kill ring,
  903.             pushing the current kill buffer and clearing
  904.             what will be the new kill buffer
  905. */
  906.  
  907. VOID PASCAL NEAR next_kill()
  908.  
  909. {
  910.     /* advance to the next kill ring entry */
  911.     kill_index++;
  912.     if (kill_index == NRING)
  913.         kill_index = 0;
  914.  
  915.     /* and clear it, so it is ready for use */
  916.     kdelete();
  917. }
  918.  
  919. /*
  920.  * Insert a character to the kill buffer, allocating new chunks as needed.
  921.  * Return TRUE if all is well, and FALSE on errors.
  922.  */
  923.  
  924. #if    PROTO
  925. int PASCAL NEAR kinsert(int direct, char c)
  926. #else
  927. int PASCAL NEAR kinsert(direct, c)
  928.  
  929. int direct;    /* direction (FORWARD/REVERSE) to insert characters */
  930. char c;        /* character to insert in the kill buffer */
  931. #endif
  932.  
  933. {
  934.     KILL *nchunk;    /* ptr to newly malloced chunk */
  935.  
  936.     if (direct == FORWARD) {
  937.  
  938.         /* check to see if we need a new chunk */
  939.         if (kused[kill_index] >= KBLOCK) {
  940.             if ((nchunk = (KILL *)malloc(sizeof(KILL))) == NULL) {
  941.                 mlwrite(TEXT94);
  942. /*                    "%%Out of memory" */
  943.                 return(FALSE);
  944.             }
  945.             if (kbufh[kill_index] == NULL)    /* set head ptr if first time */
  946.                 kbufh[kill_index] = nchunk;
  947.             if (kbufp[kill_index] != NULL)    /* point the current to this new one */
  948.                 kbufp[kill_index]->d_next = nchunk;
  949.             kbufp[kill_index] = nchunk;
  950.             kbufp[kill_index]->d_next = NULL;
  951.             kused[kill_index] = 0;
  952. #if    WINDOW_MSWIN
  953.             {
  954.                 static int o = 0;
  955.                 if (--o < 0) {
  956.                     longop(TRUE);
  957.                     o = 10;     /* to lower overhead,
  958.                         only 10% calls to longop */
  959.                 }
  960.             }
  961. #endif
  962.         }
  963.     
  964.         /* and now insert the character */
  965.         kbufp[kill_index]->d_chunk[kused[kill_index]++] = c;
  966.     } else {
  967.         /* REVERSE */
  968.         /* check to see if we need a new chunk */
  969.         if (kskip[kill_index] == 0) {
  970.             if ((nchunk = (KILL *)malloc(sizeof(KILL))) == NULL) {
  971.                 mlwrite(TEXT94);
  972. /*                    "%%Out of memory" */
  973.                 return(FALSE);
  974.             }
  975.             if (kbufh[kill_index] == NULL) {    /* set head ptr if first time */
  976.                 kbufh[kill_index] = nchunk;
  977.                 kbufp[kill_index] = nchunk;
  978.                 kskip[kill_index] = KBLOCK;
  979.                 kused[kill_index] = KBLOCK;
  980.                 nchunk->d_next = (KILL *)NULL;
  981.             } else {
  982.                 nchunk->d_next = kbufh[kill_index];
  983.                 kbufh[kill_index] = nchunk;
  984.                 kskip[kill_index] = KBLOCK;
  985.             }
  986. #if    WINDOW_MSWIN
  987.             {
  988.                 static int o = 0;
  989.                 if (--o < 0) {
  990.                     longop(TRUE);
  991.                     o = 10;     /* to lower overhead,
  992.                         only 10% calls to longop */
  993.                 }
  994.             }
  995. #endif
  996.         }
  997.     
  998.         /* and now insert the character */
  999.         kbufh[kill_index]->d_chunk[--kskip[kill_index]] = c;
  1000.     }
  1001.     return(TRUE);
  1002. }
  1003.  
  1004. /*
  1005.  * Yank text back from the kill buffer. This is really easy. All of the work
  1006.  * is done by the standard insert routines. All you do is run the loop, and
  1007.  * check for errors. Bound to "C-Y".
  1008.  */
  1009.  
  1010. #define    Char_insert(a)    (a == '\r' ? lnewline() : linsert(1, a))
  1011.  
  1012. int PASCAL NEAR yank(f, n)
  1013.  
  1014. int f,n;    /* prefix flag and argument */
  1015.  
  1016. {
  1017.     register int counter;    /* counter into kill buffer data */
  1018.     register char *sp;    /* pointer into string to insert */
  1019.     short int curoff;    /* storage for line before yanking */
  1020.     LINE *curline;
  1021.     KILL *kptr;        /* pointer into kill buffer */
  1022.  
  1023.     if (curbp->b_mode&MDVIEW)    /* don't allow this command if    */
  1024.         return(rdonly());    /* we are in read only mode    */
  1025.     if (n < 0)
  1026.         return(FALSE);
  1027.  
  1028.     /* make sure there is something to yank */
  1029.     if (kbufh[kill_index] == NULL) {
  1030.         last_size = 0L;
  1031.         return(TRUE);        /* not an error, just nothing */
  1032.     }
  1033.  
  1034.     /*
  1035.      * Save the local pointers to hold global ".".
  1036.      */
  1037.     if (yankflag) {
  1038.         /* Find the *previous* line, since the line we are on
  1039.          * may disappear due to re-allocation.  This works even
  1040.          * if we are on the first line of the file.
  1041.          */
  1042.         curline = lback(curwp->w_dotp);
  1043.         curoff = curwp->w_doto;
  1044.     }
  1045.  
  1046.     /* for each time.... */
  1047.     while (n--) {
  1048.         last_size = 0L;
  1049.         if (kskip[kill_index] > 0) {
  1050.             kptr = kbufh[kill_index];
  1051.             sp = &(kptr->d_chunk[kskip[kill_index]]);
  1052.             counter = kskip[kill_index];
  1053.             while (counter++ < KBLOCK) {
  1054.                 Char_insert(*sp);
  1055.                 last_size++;
  1056.                 ++sp;
  1057.             }
  1058.             kptr = kptr->d_next;
  1059.         } else {
  1060.             kptr = kbufh[kill_index];
  1061.         }
  1062.     
  1063.         if (kptr != (KILL *)NULL) {
  1064.             while (kptr != kbufp[kill_index]) {
  1065.                 sp = kptr->d_chunk;
  1066.                 for(counter = 0; counter < KBLOCK; counter++) {
  1067.                     Char_insert(*sp);
  1068.                     last_size++;
  1069.                     ++sp;
  1070.                 }
  1071.                 kptr = kptr->d_next;
  1072.             }
  1073.             counter = kused[kill_index];
  1074.             sp = kptr->d_chunk;
  1075.             while (counter--) {
  1076.                 Char_insert(*sp);
  1077.                 last_size++;
  1078.                 ++sp;
  1079.             }
  1080.         }
  1081.     }
  1082.  
  1083.     /* If requested, set global "." back to the
  1084.      * beginning of the yanked text.
  1085.      */
  1086.     if (yankflag) {
  1087.         curwp->w_dotp = lforw(curline);
  1088.         curwp->w_doto = curoff;
  1089.     }
  1090.     thisflag |= CFYANK;
  1091.     return(TRUE);
  1092. }
  1093.  
  1094. int PASCAL NEAR cycle_ring(f, n)
  1095.  
  1096. int f,n;    /* prefix flag and argument */
  1097.  
  1098. {
  1099.     register int orig_index;    /* original kill_index */
  1100.  
  1101.     /* if there is an argument, cycle the kill index */
  1102.     if (f) {
  1103.         while (n) {
  1104.             orig_index = kill_index;
  1105.             do {
  1106.                 kill_index--;
  1107.                 if (kill_index < 0)
  1108.                     kill_index = NRING - 1;
  1109.             } while ((orig_index != kill_index) &&
  1110.                 (kbufh[kill_index] == (KILL *)NULL));
  1111.             n--;
  1112.         }
  1113.     }
  1114.     return TRUE;
  1115. }
  1116.  
  1117. int PASCAL NEAR yank_pop(f, n)
  1118.  
  1119. int f,n;    /* prefix flag and argument */
  1120.  
  1121. {
  1122.     /* defaulted non first call will cycle by 1 */
  1123.     if ((lastflag & CFYANK) && (f == FALSE)) {
  1124.         f = TRUE;
  1125.         n = 1;
  1126.     }
  1127.  
  1128.     /* cycle the kill ring appropriately */
  1129.     cycle_ring(f, n);
  1130.  
  1131.     /* if not the first consectutive time, delete the last yank */
  1132.     if ((lastflag & CFYANK))
  1133.         ldelete(-last_size, FALSE);
  1134.  
  1135.     /* and insert the current kill buffer */
  1136.     return(yank(FALSE, 1));
  1137. }
  1138.  
  1139. int PASCAL NEAR clear_ring(f, n)
  1140.  
  1141. int f,n;    /* prefix flag and argument */
  1142.  
  1143. {
  1144.     register int index;
  1145.  
  1146.     for (index = 0; index < NRING; index++)
  1147.         next_kill();
  1148.     mlwrite(TEXT228);
  1149. /*        "[Kill ring cleared]" */
  1150.     return(TRUE);
  1151. }
  1152.  
  1153. #if    0
  1154. dispkill()
  1155.  
  1156. {
  1157.     KILL *kptr;
  1158.     int index;
  1159.     char *sp;
  1160.     int counter;
  1161.  
  1162.     if (kbufh[kill_index] == (KILL *)NULL) {
  1163.         printf("<EMPTY>\n");
  1164.         return;
  1165.     }
  1166.  
  1167.     index = 1;
  1168.     if (kskip[kill_index] > 0) {
  1169.         kptr = kbufh[kill_index];
  1170.         printf("kskip[kill_index] = %d\nBLOCK %d <", kskip[kill_index], index++);
  1171.         sp = &(kptr->d_chunk[kskip[kill_index]]);
  1172.         counter = kskip[kill_index];
  1173.         while (counter++ < KBLOCK) {
  1174.             putchar(*sp++);
  1175.         }
  1176.         printf(">\n");
  1177.         kptr = kptr->d_next;
  1178.     } else {
  1179.         kptr = kbufh[kill_index];
  1180.     }
  1181.  
  1182.     if (kptr != (KILL *)NULL) {
  1183.         while (kptr != kbufp[kill_index]) {
  1184.             printf("BLOCK %d <%255s>\n", index++, kptr->d_chunk);
  1185.             kptr = kptr->d_next;
  1186.         }
  1187.         printf("BLOCK %d <", index++);
  1188.         counter = kused[kill_index];
  1189.         sp = kptr->d_chunk;
  1190.         while (counter--) {
  1191.             putchar(*sp++);
  1192.         }
  1193.         printf(">\nkused[kill_index] = %d\n", kused[kill_index]);
  1194.     }
  1195.  
  1196. }
  1197. #endif
  1198.